﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Surging.Core.CPlatform;

namespace Surging.Base
{
    public class ReflectionHelpers
    {

        public static List<Assembly> _referenceAssembly = new List<Assembly>();
        public static List<Assembly> GetReferenceAssembly(params string[] virtualPaths)
        {
            var rootPath = AppContext.BaseDirectory;
            var existsPath = virtualPaths.Any();
            if (existsPath && !string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath))
                rootPath = AppConfig.ServerOptions.RootPath;
            var result = _referenceAssembly;
            if (!result.Any() || existsPath)
            {
                var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList();
                if (!existsPath) paths.Add(rootPath);
                paths.ForEach(path =>
                {
                    var assemblyFiles = GetAllAssemblyFiles(path);
                    foreach (var referencedAssemblyFile in assemblyFiles)
                    {
                        var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile);
                        if (!_referenceAssembly.Contains(referencedAssembly))
                            _referenceAssembly.Add(referencedAssembly);
                    }
                    result = _referenceAssembly;
                });
            }
            return result;
        }


        private static List<string> GetAllAssemblyFiles(string parentDir)
        {
            var notRelatedFile = AppConfig.ServerOptions.NotRelatedAssemblyFiles;
            var relatedFile = AppConfig.ServerOptions.RelatedAssemblyFiles;
            var pattern = string.Format("^Microsoft.\\w*|^System.\\w*|^Netty.\\w*|^Autofac.\\w*|Surging.Core.\\w*{0}",
                string.IsNullOrEmpty(notRelatedFile) ? "" : $"|{notRelatedFile}");
            Regex notRelatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            Regex relatedRegex = new Regex(relatedFile, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            if (!string.IsNullOrEmpty(relatedFile))
            {
                return
                    Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where(
                        a => !notRelatedRegex.IsMatch(a) && relatedRegex.IsMatch(a)).ToList();
            }
            else
            {
                return
                    Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where(
                        a => !notRelatedRegex.IsMatch(a)).ToList();
            }
        }

        public static Type GetReturnType(MethodInfo method)
        {
            if (method.ReturnType.IsConstructedGenericType)
            {
                return method.ReturnType.GenericTypeArguments.FirstOrDefault();
            }
            return method.ReturnType;
        }

        public static Dictionary<string, object> GetParameterTypes(Dictionary<string, object> items, Type type, ParameterInfo[] parm)
        {
            var childs = new List<Type>();
            if (items.All(y => y.Key != type.Name))
            {
                var propinfos = GetPorpertiesInfo(type.GetProperties());
                childs.AddRange(propinfos.childs);
                items.Add(type.Name, new
                {
                    type = GetBaseType(type).Name,
                    properties = propinfos.propertiesitem
                });
            }
            parm.ToList().ForEach(y =>
            {
                if (items.All(m => m.Key != y.ParameterType.Name))
                {
                    var propinfos = GetPorpertiesInfo(y.ParameterType.GetProperties());
                    childs.AddRange(propinfos.childs);
                    items.Add(y.ParameterType.Name, new
                    {
                        type = GetBaseType(y.ParameterType.BaseType).Name,
                        properties = new Dictionary<string, object> {{y.Name, new
                        {
                            type = GetBaseType(y.ParameterType.BaseType).Name.ToLower(),
                            properties = propinfos.propertiesitem
                        }}}
                    });
                }
            });
            if (childs.Any())
            {
                items = GetChidParameterTypes(items, childs);
            }
            return items;
        }
        public static Dictionary<string, object> GetChidParameterTypes(Dictionary<string, object> items, List<Type> type)
        {
            var childs = new List<Type>();
            type.ForEach(y =>
            {
                if (items.All(m => m.Key != y.Name))
                {
                    var propinfos = GetPorpertiesInfo(y.GetProperties());
                    childs.AddRange(propinfos.childs);
                    items.Add(y.Name, new
                    {
                        type = GetBaseType(y).Name,
                        properties = propinfos.propertiesitem
                    });
                }
            });
            if (childs.Any())
            {
                items = GetChidParameterTypes(items, childs);
            }
            return items;
        }

        public static (Dictionary<string, object> propertiesitem, List<Type> childs) GetPorpertiesInfo(PropertyInfo[] infos)
        {
            var propertiesitem = new Dictionary<string, dynamic>();
            var childalls = new List<Type>();
            infos.ToList().ForEach(z =>
            {
                propertiesitem.Add(z.Name, GetParameter(z.PropertyType,out List<Type> childs));
                childalls.AddRange(childs);
            });
            return (propertiesitem, childalls);
        }

        public static ExpandoObject GetParameter(Type type,out List<Type> childs)
        {
            dynamic result = new ExpandoObject();
            childs = new List<Type>();
            if (type.IsPrimitive)//基元类型
            {
                if (type.Name ==
                    typeof(Int16).Name || type.Name == typeof(int).Name || type.Name == typeof(short).Name ||
                    type.Name == typeof(Int32).Name || type.Name == typeof(Int64).Name ||
                    type.Name == typeof(long).Name)
                {
                    result.type = "integer";
                    result.format = type.Name;
                }
                else if(type.Name == typeof(bool).Name || type.Name == typeof(Boolean).Name)
                {
                    result.type = "boolean";
                }
                else
                {
                    result.type = "number";
                    result.format = "double";
                }
            }
            else if (type == typeof(object) || type == typeof(string))
            {
                result.type = type.Name.ToLower();
            }
            else if (type == typeof(Guid))
            {
                result.type = "string";
                result.format = "uuid";
            }
            else if (type == typeof(DateTime))
            {
                result.type = "string";
                result.format = "date-time";
            }
            else if (type.IsEnum)//枚举类型
            {
                result.type = "integer";
                result.format = "int32";
            }
            else if (type.IsConstructedGenericType) //泛型
            {
                if (type.Name == "Nullable`1")
                {
                    result = GetParameter(type.GenericTypeArguments.FirstOrDefault(), out List<Type> child);
                }
                else
                {
                    result.type = "array";
                    var listtype = type.GenericTypeArguments.FirstOrDefault();
                    result.items = GetParameter(listtype, out List<Type> child);
                    childs.AddRange(child);
                }
            }
            else if (type.IsArray) //数组
            {
                result.type = "array";
                var arrayname = type.FullName.Replace("[]", "");
                var arrtype = Type.GetType(arrayname);
                if (arrtype != null)
                {
                    result.items = GetParameter(arrtype, out List<Type> child);
                    childs.AddRange(child);
                }
            }
            else //自定义类型
            {
                childs.Add(type);
                result.type = new {refs = $"#/definitions/{type.Name}"};
            }
            return result;
        }
        public static Type GetBaseType(Type type)
        {
            if (type == typeof(Object) || type == typeof(ValueType))
            {
                return type;
            }
            else
            {
                return GetBaseType(type.BaseType);
            }
        }
    }
}
